#include <fcntl.h>
#include <getopt.h>
#include <linux/fb.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>

#include <cmath>
#include <condition_variable>
#include <chrono>
#include <future>
#include <iostream>
#include <mutex>
#include <thread>

#include <Minicap.hpp>
#include <jni.h>

#include "util/debug.h"
#include "JpgEncoder.hpp"
#include "SimpleServer.hpp"
#include "Projection.hpp"
#include "debug.h"
#include "minitouch.hpp"

#include "com_example_twolibs_TwoLibs.h"

#include <libyuv.h>

// #include "common\x264encoder.h"
// #include<vector>

#define BANNER_VERSION 4
#define BANNER_SIZE 24

#define DEFAULT_SOCKET_NAME "minicap"
#define DEFAULT_DISPLAY_ID 0
#define DEFAULT_JPG_QUALITY 100

extern void MySaveBmp(const char *filename, unsigned char *rgbbuf, int width, int height);
extern int bmp_write(unsigned char *image, int imageWidth, int imageHeight, char *filename);

extern void SaveBGR32ToBMP(char *rgbaData, int rgbaSize, int width, int height, char *bmpFile);
extern int trgb2yuv(unsigned char* rgb_buffer, int width, int height, unsigned char** yuv_buffer, int * yuv_size, int subsample);

enum {
	QUIRK_DUMB = 1,
	QUIRK_ALWAYS_UPRIGHT = 2,
	QUIRK_TEAR = 4,
};

#if 1
Minicap *minicap = NULL;
unsigned char g_quirks = 0;
Minicap::DisplayInfo realInfo;
Minicap::DisplayInfo desiredInfo;
JpgEncoder encoder(4, 0);
bool haveFrame = false;
Minicap::Frame frame;
int pending = 0;
bool orientation_flag = false;
#endif

class FrameWaiter : public Minicap::FrameAvailableListener {
public:
	FrameWaiter()
		: mPendingFrames(0),
		mTimeout(std::chrono::milliseconds(100)),
		mStopped(false) {
	}

	int
		waitForFrame() {

		//printf("waitForFrame 1 %d\n", mPendingFrames);
		std::unique_lock<std::mutex> lock(mMutex);

		while (!mStopped) {
			if (mCondition.wait_for(lock, mTimeout, [this] {return mPendingFrames > 0; }))
			{
				return mPendingFrames--;
			}
		}
		//printf("waitForFrame 2\n");
		return 0;
	}

	int
		waitForFrameByTime(int itime) {

		//printf("waitForFrame 1 %d\n", mPendingFrames);
		std::unique_lock<std::mutex> lock(mMutex);

		while (!mStopped) {
			if (mCondition.wait_for(lock, std::chrono::milliseconds(itime), [this] {return mPendingFrames > 0; }))
			{
				return mPendingFrames--;
			}
			return -1;
		}
		//printf("waitForFrame 2\n");
		return 0;
	}

	void
		reportExtraConsumption(int count) {
		std::unique_lock<std::mutex> lock(mMutex);
		mPendingFrames -= count;
	}

	void
		onFrameAvailable() {
		std::unique_lock<std::mutex> lock(mMutex);
		mPendingFrames += 1;
		mCondition.notify_one();
	}

	void
		stop() {
		mStopped = true;
	}

	bool
		isStopped() {
		return mStopped;
	}

private:
	std::mutex mMutex;
	std::condition_variable mCondition;
	std::chrono::milliseconds mTimeout;
	int mPendingFrames;
	bool mStopped;
};

bool g_fd_enabled;

static int
pumps(int fd, unsigned char* data, size_t length) {
	do {
		// Make sure that we don't generate a SIGPIPE even if the socket doesn't
		// exist anymore. We'll still get an EPIPE which is perfect.
		int wrote = send(fd, data, length, MSG_NOSIGNAL);

		if (wrote < 0) {
			g_fd_enabled = false;
			printf("send %d\n",wrote);
			return wrote;
		}

		data += wrote;
		length -= wrote;
	} while (length > 0);

	return 0;
}

static int
pumpf(int fd, unsigned char* data, size_t length) {
	do {
		int wrote = write(fd, data, length);

		if (wrote < 0) {
			return wrote;
		}

		data += wrote;
		length -= wrote;
	} while (length > 0);

	return 0;
}

static int
putUInt32LE(unsigned char* data, int value) {
	data[0] = (value & 0x000000FF) >> 0;
	data[1] = (value & 0x0000FF00) >> 8;
	data[2] = (value & 0x00FF0000) >> 16;
	data[3] = (value & 0xFF000000) >> 24;
}

static int
try_get_framebuffer_display_info(uint32_t displayId, Minicap::DisplayInfo* info) {
	char path[64];
	sprintf(path, "/dev/graphics/fb%d", displayId);

	int fd = open(path, O_RDONLY);
	if (fd < 0) {
		MCERROR("Cannot open %s", path);
		return -1;
	}

	fb_var_screeninfo vinfo;
	if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
		close(fd);
		MCERROR("Cannot get FBIOGET_VSCREENINFO of %s", path);
		return -1;
	}

	info->width = vinfo.xres;
	info->height = vinfo.yres;
	info->orientation = vinfo.rotate;
	info->xdpi = static_cast<float>(vinfo.xres) / static_cast<float>(vinfo.width) * 25.4;
	info->ydpi = static_cast<float>(vinfo.yres) / static_cast<float>(vinfo.height) * 25.4;
	info->size = std::sqrt(
		(static_cast<float>(vinfo.width) * static_cast<float>(vinfo.width)) +
		(static_cast<float>(vinfo.height) * static_cast<float>(vinfo.height))) / 25.4;
	info->density = std::sqrt(
		(static_cast<float>(vinfo.xres) * static_cast<float>(vinfo.xres)) +
		(static_cast<float>(vinfo.yres) * static_cast<float>(vinfo.yres))) / info->size;
	info->secure = false;
	info->fps = 0;

	close(fd);

	return 0;
}

static FrameWaiter gWaiter;

static void
signal_handler(int signum) {
	switch (signum) {
	case SIGINT:
		MCINFO("Received SIGINT, stopping");
		gWaiter.stop();
		break;
	case SIGTERM:
		MCINFO("Received SIGTERM, stopping");
		gWaiter.stop();
		break;
	default:
		abort();
		break;
	}
}

int lastTimeMsec = 0;
int getMsec()
{
	struct timeval start;
	gettimeofday(&start, NULL);
	int  i_ret = (start.tv_sec % 10000) * 1000 + start.tv_usec / 1000;
	return i_ret;
}
//�Ƿ���֡
bool isSkipFps()
{
	bool bret = true;
	int nowTimeMsec = 0;
	nowTimeMsec = getMsec();
	//�����ǰʱ�䳬��33���� ��ô�ͷ��ͻ���
	if (nowTimeMsec > lastTimeMsec + 32)
	{
		bret = false;
		lastTimeMsec = nowTimeMsec;
		return bret;
	}
	//�����ͻ���
	//printf("lastTimeMsec:%08d nowTimeMsec:%08d\n", lastTimeMsec, nowTimeMsec);
	return bret;
}


//��ʾ1�뷢�Ͷ�����ͼƬ
int lastTimeSec = 0;
int showSum = 0;
int ibitsum = 0;
void showMsg(int datalenth)
{
	int nowTimeSec = 0;
	showSum++;
	nowTimeSec = getMsec();
	ibitsum += datalenth;
	if (nowTimeSec > lastTimeSec + 1000)
	{
		printf("last:%08d now:%08d fps:%d bit/s:%f\n", lastTimeSec, nowTimeSec, showSum, (float)ibitsum / 1000);
		lastTimeSec = nowTimeSec;
		ibitsum = 0;
		showSum = 0;
	}
}

void test()
{

	printf("test\n");
	printf("star %d\n", getMsec());
	sleep(1);
	printf("end %d\n", getMsec());
}

//д�ļ�
int api_WriteFile(char * filename, const char * mode, char * pbuf, int lenth)
{
	FILE *fp;
	int ret;
	fp = fopen(filename, mode);
	if (fp == NULL)
	{
		perror("fopen");
		return -1;
	}
	if (fwrite(pbuf, lenth, sizeof(char), fp) == -1)
	{
		perror("fwrite");
		return -1;
	}
	//printf("fwrite is count %d %s\r\n",lenth,filename);
	fclose(fp);
}

//д�ļ�
void writefile(int i_num, int i_type, char * buf, int lenth)
{

	char str_buf[1000] = { 0 };
	if (i_type == 1)
	{
		sprintf(str_buf, "/sdcard/img/%03d.rgb", i_num);
		api_WriteFile(str_buf, "w+", buf, lenth);
	}
	else if (i_type == 2)
	{
		sprintf(str_buf, "/sdcard/img/%03d.jpg", i_num);
		api_WriteFile(str_buf, "w+", buf, lenth);
	}
	else if (i_type == 3)
	{
		sprintf(str_buf, "/sdcard/img/%03d.yuv", i_num);
		api_WriteFile(str_buf, "w+", buf, lenth);
	}
}


int g_inum = 0;
int fd;


void callback(void* pdata, int datalen)
{
	//printf("%p %d %d \n", pdata, datalen, g_inum);
	//api_WriteFile("/data/local/tmp/test.h264", (const char *)"a+", (char*)pdata, datalen);
	pumps(fd, (unsigned char *)pdata, datalen);
}


#if 1

jbyteArray JNICALL Java_jp_co_cyberagent_stf_App_get_1screen(JNIEnv * env, jobject thiz, jint realInfo_width, jint realInfo_height, jint desiredInfo_width, jint desiredInfo_height, jint desiredInfo_orientation, jint quality = DEFAULT_JPG_QUALITY)
{
	int size = 0;
	unsigned char* data = NULL;
	int err;
	JpgEncoder s_encoder(4, 0);
	Minicap::Frame s_frame;
	uint32_t displayId = DEFAULT_DISPLAY_ID;
	Minicap::DisplayInfo s_desiredInfo;
	Minicap::DisplayInfo s_realInfo;
	Minicap* s_minicap = minicap_create(displayId);
	FrameWaiter s_gWaiter;

	minicap_start_thread_pool();

	s_realInfo.width = realInfo_width;
	s_realInfo.height = realInfo_height;

	s_desiredInfo.width = desiredInfo_width;
	s_desiredInfo.height = desiredInfo_height;
	s_desiredInfo.orientation = desiredInfo_orientation;

	if (s_minicap == NULL) {
		return NULL;
	}

	if (s_minicap->setRealInfo(s_realInfo) != 0) {
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%s", "Minicap did not accept real display info");

		minicap_free(s_minicap);
		return NULL;
	}

	if (s_minicap->setDesiredInfo(s_desiredInfo) != 0) {
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%s", "Minicap did not accept desired display info");

		minicap_free(s_minicap);
		return NULL;
	}

	s_minicap->setFrameAvailableListener(&s_gWaiter);

	if (s_minicap->applyConfigChanges() != 0) {
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%s", "Unable to start s_minicap with current config");

		minicap_free(s_minicap);
		return NULL;
	}

	if (!s_encoder.reserveData(s_realInfo.width, s_realInfo.height)) {
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%s", "Unable to reserve data for JPG s_encoder");

		minicap_free(s_minicap);
		return NULL;
	}

	if (!s_gWaiter.waitForFrame()) {
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%s", "Unable to wait for s_frame");

		minicap_free(s_minicap);
		return NULL;
	}

	if ((err = s_minicap->consumePendingFrame(&s_frame)) != 0) {
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%s", "Unable to consume pending s_frame");

		minicap_free(s_minicap);
		return NULL;
	}

	if (!s_encoder.encode(&s_frame, quality)) {
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%s", "Unable to encode frame");

		minicap_free(s_minicap);
		return NULL;
	}

	size = s_encoder.getEncodedSize();
	data = s_encoder.getEncodedData();

	jbyteArray c_result = env->NewByteArray(s_encoder.getEncodedSize());
	env->SetByteArrayRegion(c_result, 0, size, (jbyte*)data);

	s_minicap->releaseConsumedFrame(&frame);

	s_gWaiter.stop();
	s_minicap->release();
	minicap_free(s_minicap);
	s_minicap = NULL;

	return c_result;
}

unsigned char* yuv_buffer = NULL;
int yuv_size = 0;
JNIEXPORT jbyteArray JNICALL Java_jp_co_cyberagent_stf_App_get_1cap(JNIEnv * env, jobject thiz, jint quality = DEFAULT_JPG_QUALITY)
{
	int main_sec, trgb2yuv_sec, start,end,start_yuv,end_yuv, p_start,p_end,pendingframe_sec,cpy_start,cpy_end,cpy_sec;
	// LOGV("version: %s", MINICAP_VERSION);
	start = getMsec();
    if (minicap == NULL)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is NULL");
        return NULL;
    }

    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is %08x ---> gWaiter.waitForFrame %d ---", minicap, gWaiter.isStopped());

    // while (!gWaiter.isStopped() && (pending = gWaiter.waitForFrame()) > 0)
	if (!gWaiter.isStopped()) {
		g_fd_enabled = true;

		int err;

		while (!gWaiter.isStopped()) {
			pending = gWaiter.waitForFrameByTime(30);
			print_debug_info(__FILE__, __LINE__, __FUNCTION__, "pending: %d yuv_buffer: %08x flag: %d\r\n", pending, yuv_buffer, !orientation_flag);

			if (pending > 0)
			{
				p_start = getMsec();
				if ((err = minicap->consumePendingFrame(&frame)) != 0) {
					if (err == -EINTR) {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Frame consumption interrupted by EINTR");
						goto close;
					}
					else {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to consume pending frame");
						goto close;
					}
				}
				p_end = getMsec();
				pendingframe_sec = p_end-p_start;
				haveFrame = true;
				orientation_flag = false;
				start_yuv = getMsec();
				// libyuv::ARGBToI420(argb_buff, width*4, yuv_y_buff, width, yuv_u_buff, width / 2, yuv_v_buff, width / 2, width, height)
				// libyuv::ScaleARGBDown2(width,height, )
				trgb2yuv((unsigned char*)frame.data, frame.width, frame.height, &yuv_buffer, &yuv_size, 2);
				end_yuv = getMsec();
				trgb2yuv_sec = end_yuv-start_yuv;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__, "yuv_size: %d data:0x%08x %dx%d\r\n", yuv_size, frame.data, frame.width, frame.height);

				cpy_start = getMsec();
				jbyteArray c_result = env->NewByteArray(yuv_size);
				env->SetByteArrayRegion(c_result, 0, yuv_size, (jbyte*)yuv_buffer);

				minicap->releaseConsumedFrame(&frame);
				cpy_end = getMsec();
				cpy_sec = cpy_end-cpy_start;
				haveFrame = false;
				end = getMsec();
				main_sec = end-start;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms trgb2yuv_sec: %dms pendingframe_sec: %dms cpy_sec: %dms\r\n", main_sec, trgb2yuv_sec, pendingframe_sec, cpy_sec);
				return c_result;
			}
			else if (pending == -1 && yuv_buffer != NULL && !orientation_flag) {
				// int yuv_size = 0;
				// start_yuv = getMsec();
				// trgb2yuv((unsigned char*)frame.data, frame.width, frame.height, &yuv_buffer, &yuv_size, 2);
				// end_yuv = getMsec();
				// trgb2yuv_sec = end_yuv-start_yuv;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__, "yuv_size: %d data:0x%08x %dx%d\r\n", yuv_size, frame.data, frame.width, frame.height);
				cpy_start = getMsec();
				jbyteArray c_result = env->NewByteArray(yuv_size);
				env->SetByteArrayRegion(c_result, 0, yuv_size, (jbyte*)yuv_buffer);

				// minicap->releaseConsumedFrame(&frame);
				cpy_end = getMsec();
				cpy_sec = cpy_end-cpy_start;

				haveFrame = false;
				end = getMsec();
				main_sec = end-start;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms pendingframe_sec: %dms \r\n", main_sec, pendingframe_sec);
				return c_result;
				// return NULL;
			}
		}

		close:
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "********************************get_cap error!!!********************************");

		// Have we consumed one frame but are still holding it?
		if (haveFrame) {
			minicap->releaseConsumedFrame(&frame);
		}
		return NULL;
		// minicap_free(minicap);
	}

    return NULL;
}

JNIEXPORT jint JNICALL Java_jp_co_cyberagent_stf_App_get_1cap_1new(JNIEnv * env, jobject thiz, jbyteArray data, jint value)
{
	int main_sec, trgb2yuv_sec, start,end,start_yuv,end_yuv, p_start,p_end,pendingframe_sec,cpy_start,cpy_end,cpy_sec;
	// LOGV("version: %s", MINICAP_VERSION);
	start = getMsec();
    if (minicap == NULL)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is NULL");
        return 0;
    }

    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is %08x ---> gWaiter.waitForFrame %d ---", minicap, gWaiter.isStopped());

    // while (!gWaiter.isStopped() && (pending = gWaiter.waitForFrame()) > 0)
	if (!gWaiter.isStopped()) {
		g_fd_enabled = true;

		int err;

		while (!gWaiter.isStopped()) {
			pending = gWaiter.waitForFrameByTime(30);
			print_debug_info(__FILE__, __LINE__, __FUNCTION__, "pending: %d yuv_buffer: %08x flag: %d\r\n", pending, yuv_buffer, !orientation_flag);

			if (pending > 0)
			{
				p_start = getMsec();
				if ((err = minicap->consumePendingFrame(&frame)) != 0) {
					if (err == -EINTR) {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Frame consumption interrupted by EINTR");
						goto close;
					}
					else {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to consume pending frame");
						goto close;
					}
				}
				p_end = getMsec();
				pendingframe_sec = p_end-p_start;
				haveFrame = true;
				orientation_flag = false;
				start_yuv = getMsec();
				// libyuv::ARGBToI420(argb_buff, width*4, yuv_y_buff, width, yuv_u_buff, width / 2, yuv_v_buff, width / 2, width, height)
				// libyuv::ScaleARGBDown2(width,height, )
				trgb2yuv((unsigned char*)frame.data, frame.width, frame.height, &yuv_buffer, &yuv_size, 2);
				end_yuv = getMsec();
				trgb2yuv_sec = end_yuv-start_yuv;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__, "yuv_size: %d data:0x%08x %dx%d\r\n", yuv_size, frame.data, frame.width, frame.height);

				cpy_start = getMsec();
				jbyte* c_result = env->GetByteArrayElements(data, 0);
				memcpy(c_result, yuv_buffer, yuv_size);
				env->ReleaseByteArrayElements(data, c_result, 0);
				// env->SetByteArrayRegion(c_result, 0, yuv_size, (jbyte*)yuv_buffer);

				minicap->releaseConsumedFrame(&frame);
				cpy_end = getMsec();
				cpy_sec = cpy_end-cpy_start;
				haveFrame = false;
				end = getMsec();
				main_sec = end-start;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms trgb2yuv_sec: %dms pendingframe_sec: %dms cpy_sec: %dms\r\n", main_sec, trgb2yuv_sec, pendingframe_sec, cpy_sec);
				return yuv_size;
			}
			else if (pending == -1 && yuv_buffer != NULL && !orientation_flag) {
				// int yuv_size = 0;
				// start_yuv = getMsec();
				// trgb2yuv((unsigned char*)frame.data, frame.width, frame.height, &yuv_buffer, &yuv_size, 2);
				// end_yuv = getMsec();
				// trgb2yuv_sec = end_yuv-start_yuv;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__, "yuv_size: %d data:0x%08x %dx%d\r\n", yuv_size, frame.data, frame.width, frame.height);
				cpy_start = getMsec();
				// jbyteArray c_result = env->NewByteArray(yuv_size);
				// env->SetByteArrayRegion(c_result, 0, yuv_size, (jbyte*)yuv_buffer);
				jbyte* c_result = env->GetByteArrayElements(data, 0);
				memcpy(c_result, yuv_buffer, yuv_size);
				env->ReleaseByteArrayElements(data, c_result, 0);

				// minicap->releaseConsumedFrame(&frame);
				cpy_end = getMsec();
				cpy_sec = cpy_end-cpy_start;

				haveFrame = false;
				end = getMsec();
				main_sec = end-start;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms pendingframe_sec: %dms cpy_sec: %dms\r\n", main_sec, pendingframe_sec, cpy_sec);
				return yuv_size;
				// return NULL;
			}
		}

		close:
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "********************************get_cap error!!!********************************");

		// Have we consumed one frame but are still holding it?
		if (haveFrame) {
			minicap->releaseConsumedFrame(&frame);
		}
		return 0;
		// minicap_free(minicap);
	}

    return 0;
}

jbyteArray last_byteArray = NULL;
JNIEXPORT jbyteArray JNICALL Java_jp_co_cyberagent_stf_App_get_1cap_1null(JNIEnv * env, jobject thiz, 	jboolean ret_value)
{
	int main_sec, trgb2yuv_sec, start,end,start_yuv,end_yuv, p_start,p_end,pendingframe_sec,cpy_start,cpy_end,cpy_sec;
	// LOGV("version: %s", MINICAP_VERSION);
	start = getMsec();
    if (minicap == NULL)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is NULL");
        return NULL;
    }

    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is %08x ---> gWaiter.waitForFrame %d ---", minicap, gWaiter.isStopped());

    // while (!gWaiter.isStopped() && (pending = gWaiter.waitForFrame()) > 0)
	if (!gWaiter.isStopped()) {
		g_fd_enabled = true;

		int err;

		while (!gWaiter.isStopped()) {
			pending = gWaiter.waitForFrameByTime(30);
			print_debug_info(__FILE__, __LINE__, __FUNCTION__, "pending: %d yuv_buffer: %08x flag: %d\r\n", pending, yuv_buffer, !orientation_flag);

			if (pending > 0)
			{
				p_start = getMsec();
				if ((err = minicap->consumePendingFrame(&frame)) != 0) {
					if (err == -EINTR) {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Frame consumption interrupted by EINTR");
						goto close;
					}
					else {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to consume pending frame");
						goto close;
					}
				}
				p_end = getMsec();
				pendingframe_sec = p_end-p_start;
				haveFrame = true;
				orientation_flag = false;
				start_yuv = getMsec();
				// libyuv::ARGBToI420(argb_buff, width*4, yuv_y_buff, width, yuv_u_buff, width / 2, yuv_v_buff, width / 2, width, height)
				// libyuv::ScaleARGBDown2(width,height, )
				trgb2yuv((unsigned char*)frame.data, frame.width, frame.height, &yuv_buffer, &yuv_size, 2);
				end_yuv = getMsec();
				trgb2yuv_sec = end_yuv-start_yuv;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__, "yuv_size: %d data:0x%08x %dx%d\r\n", yuv_size, frame.data, frame.width, frame.height);

				cpy_start = getMsec();
				jbyteArray c_result = env->NewByteArray(yuv_size);
				env->SetByteArrayRegion(c_result, 0, yuv_size, (jbyte*)yuv_buffer);

				minicap->releaseConsumedFrame(&frame);
				cpy_end = getMsec();
				cpy_sec = cpy_end-cpy_start;
				haveFrame = false;
				end = getMsec();
				main_sec = end-start;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms trgb2yuv_sec: %dms pendingframe_sec: %dms cpy_sec: %dms\r\n", main_sec, trgb2yuv_sec, pendingframe_sec, cpy_sec);
				return c_result;
			}
			else if (pending == -1 && yuv_buffer != NULL && !orientation_flag) {
				if (ret_value) {
					// int yuv_size = 0;
					// start_yuv = getMsec();
					// trgb2yuv((unsigned char*)frame.data, frame.width, frame.height, &yuv_buffer, &yuv_size, 2);
					// end_yuv = getMsec();
					// trgb2yuv_sec = end_yuv-start_yuv;
					print_debug_info(__FILE__, __LINE__, __FUNCTION__, "yuv_size: %d data:0x%08x %dx%d\r\n", yuv_size, frame.data, frame.width, frame.height);
					cpy_start = getMsec();
					jbyteArray c_result = env->NewByteArray(yuv_size);
					env->SetByteArrayRegion(c_result, 0, yuv_size, (jbyte*)yuv_buffer);

					// minicap->releaseConsumedFrame(&frame);
					cpy_end = getMsec();
					cpy_sec = cpy_end-cpy_start;

					haveFrame = false;
					end = getMsec();
					main_sec = end-start;
					print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms pendingframe_sec: %dms \r\n", main_sec, pendingframe_sec);
					return c_result;
				}
				else {
					print_debug_info(__FILE__, __LINE__, __FUNCTION__,"return NULL");
					return NULL;
				}
			}
		}

		close:
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "********************************get_cap error!!!********************************");

		// Have we consumed one frame but are still holding it?
		if (haveFrame) {
			minicap->releaseConsumedFrame(&frame);
		}
		return NULL;
		// minicap_free(minicap);
	}

    return NULL;
}

jbyteArray JNICALL Java_jp_co_cyberagent_stf_App_get_1cap_1no_1yuv(JNIEnv *env, jobject, jint) {
	int main_sec, trgb2yuv_sec, start,end,start_yuv,end_yuv, p_start,p_end,pendingframe_sec,cpy_start,cpy_end,cpy_sec;
	start = getMsec();
    if (minicap == NULL)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is NULL");
        return NULL;
    }

    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is %08x", minicap);
    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "---> gWaiter.waitForFrame %d ---", gWaiter.isStopped());

    // while (!gWaiter.isStopped() && (pending = gWaiter.waitForFrame()) > 0)
	if (!gWaiter.isStopped()) {
		g_fd_enabled = true;

		int err;

		if (!gWaiter.isStopped()) {
			pending = gWaiter.waitForFrameByTime(30);
			print_debug_info(__FILE__, __LINE__, __FUNCTION__, "pending : %d\r\n", pending);

			int y_size = realInfo.width*realInfo.height;
			if (pending > 0)
			{
				p_start = getMsec();
				if ((err = minicap->consumePendingFrame(&frame)) != 0) {
					if (err == -EINTR) {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Frame consumption interrupted by EINTR");
						goto close;
					}
					else {
						print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to consume pending frame");
						goto close;
					}
				}
				p_end = getMsec();
				pendingframe_sec = p_end-p_start;
				haveFrame = true;
				orientation_flag = false;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__, "size: %d data:0x%08x %dx%d\r\n", frame.size, frame.data, frame.width, frame.height);

				cpy_start = getMsec();
				jbyteArray c_result = env->NewByteArray(frame.size);
				env->SetByteArrayRegion(c_result, 0, frame.size, (jbyte*)frame.data);

				minicap->releaseConsumedFrame(&frame);
				cpy_end = getMsec();
				cpy_sec = cpy_end-cpy_start;
				haveFrame = false;
				end = getMsec();
				main_sec = end-start;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms pendingframe_sec: %dms cpy_sec: %dms\r\n", main_sec, pendingframe_sec, cpy_sec);
				return c_result;
			}
			else if (pending == -1 && yuv_buffer != NULL && !orientation_flag){
				int yuv_size = 0;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__, "size: %d data:0x%08x %dx%d\r\n", frame.size, frame.data, frame.width, frame.height);
				cpy_start = getMsec();
				jbyteArray c_result = env->NewByteArray(yuv_size);
				env->SetByteArrayRegion(c_result, 0, frame.size, (jbyte*)frame.data);

				minicap->releaseConsumedFrame(&frame);
				cpy_end = getMsec();
				cpy_sec = cpy_end-cpy_start;
				haveFrame = false;
				end = getMsec();
				main_sec = end-start;
				print_debug_info(__FILE__, __LINE__, __FUNCTION__,"main_sec: %dms pendingframe_sec: %dms cpy_sec: %dms\r\n", main_sec, pendingframe_sec, cpy_sec);
				return c_result;
			}
		}

		close:
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "********************************get_cap error!!!********************************");

		// Have we consumed one frame but are still holding it?
		if (haveFrame) {
			minicap->releaseConsumedFrame(&frame);
		}
		return NULL;
		// minicap_free(minicap);
	}

    return NULL;
}

jint JNICALL Java_jp_co_cyberagent_stf_App_get_1banner(JNIEnv* env, jobject, jbyteArray data)
{
    print_debug_info(__FILE__, __LINE__, __FUNCTION__,"%dx%d@%dx%d/%d", realInfo.width, realInfo.height, desiredInfo.width, desiredInfo.height, desiredInfo.orientation);
    // Prepare banner for clients.
    unsigned char banner[BANNER_SIZE];
    banner[0] = (unsigned char)BANNER_VERSION;
    banner[1] = (unsigned char)BANNER_SIZE;
    putUInt32LE(banner + 2, getpid());
    putUInt32LE(banner + 6, realInfo.width);
    putUInt32LE(banner + 10, realInfo.height);
    putUInt32LE(banner + 14, desiredInfo.width);
    putUInt32LE(banner + 18, desiredInfo.height);
    banner[22] = (unsigned char)desiredInfo.orientation;
    banner[23] = g_quirks;

    env->SetByteArrayRegion(data, 0, BANNER_SIZE, (jbyte*)banner);  // copy

    return 0;
}
#endif

#if 1

jint JNICALL Java_jp_co_cyberagent_stf_App_init_1cap(JNIEnv *, jobject, jint realInfo_width, jint realInfo_height, jint desiredInfo_width, jint desiredInfo_height, jint desiredInfo_orientation)
{
    uint32_t displayId = DEFAULT_DISPLAY_ID;
    // Projection proj;
    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%dx%d@%dx%d/%d", realInfo_width, realInfo_height, desiredInfo_width, desiredInfo_height, desiredInfo_orientation);

    // Set up signal handler.
    // struct sigaction sa;
    // memset(&sa, 0, sizeof(sa));
    // sa.sa_handler = signal_handler;
    // sigemptyset(&sa.sa_mask);
    // sigaction(SIGTERM, &sa, NULL);
    // sigaction(SIGINT, &sa, NULL);

    // Start Android's thread pool so that it will be able to serve our requests.
    minicap_start_thread_pool();

	orientation_flag = false;

    // Disable STDOUT buffering.
    // setbuf(stdout, NULL);

    // Set real display size.
    realInfo.width = realInfo_width;
    realInfo.height = realInfo_height;

    // Figure out desired display size.
    desiredInfo.width = desiredInfo_width;
    desiredInfo.height = desiredInfo_height;
    desiredInfo.orientation = desiredInfo_orientation;

    // Set up minicap.
    minicap = minicap_create(displayId);
    if (minicap == NULL)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is NULL");
        return EXIT_FAILURE;
    }

    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is %08x", minicap);
    // Figure out the quirks the current capture method has.
    unsigned char quirks = 0;
    switch (minicap->getCaptureMethod())
    {
    case Minicap::METHOD_FRAMEBUFFER:
        quirks |= QUIRK_DUMB | QUIRK_TEAR;
        break;
    case Minicap::METHOD_SCREENSHOT:
        quirks |= QUIRK_DUMB;
        break;
    case Minicap::METHOD_VIRTUAL_DISPLAY:
        quirks |= QUIRK_ALWAYS_UPRIGHT;
        break;
    }

    g_quirks = quirks;

    if (minicap->setRealInfo(realInfo) != 0)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Minicap did not accept real display info");
        goto disaster;
    }

    if (minicap->setDesiredInfo(desiredInfo) != 0)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Minicap did not accept desired display info");
        goto disaster;
    }

    minicap->setFrameAvailableListener(&gWaiter);

    if (minicap->applyConfigChanges() != 0)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to start minicap with current config");
        goto disaster;
    }

    if (!encoder.reserveData(realInfo.width, realInfo.height))
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to reserve data for JPG encoder: %d x %d", realInfo.width, realInfo.height);
        goto disaster;
    }

    return EXIT_SUCCESS;

disaster:

    return EXIT_FAILURE;
}

extern "C" int init_cap(int realInfo_width, int realInfo_height, int desiredInfo_width, int desiredInfo_height, int desiredInfo_orientation)
{
    uint32_t displayId = DEFAULT_DISPLAY_ID;
    // Projection proj;
    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%dx%d@%dx%d/%d", realInfo_width, realInfo_height, desiredInfo_width, desiredInfo_height, desiredInfo_orientation);

    // Set up signal handler.
    // struct sigaction sa;
    // memset(&sa, 0, sizeof(sa));
    // sa.sa_handler = signal_handler;
    // sigemptyset(&sa.sa_mask);
    // sigaction(SIGTERM, &sa, NULL);
    // sigaction(SIGINT, &sa, NULL);

    // Start Android's thread pool so that it will be able to serve our requests.
    minicap_start_thread_pool();

	orientation_flag = true;

    // Disable STDOUT buffering.
    // setbuf(stdout, NULL);

    // Set real display size.
    realInfo.width = realInfo_width;
    realInfo.height = realInfo_height;

    // Figure out desired display size.
    desiredInfo.width = desiredInfo_width;
    desiredInfo.height = desiredInfo_height;
    desiredInfo.orientation = desiredInfo_orientation;

    // Set up minicap.
    minicap = minicap_create(displayId);
    if (minicap == NULL)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is NULL");
        return EXIT_FAILURE;
    }
    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap is %08x", minicap);
    // Figure out the quirks the current capture method has.
    unsigned char quirks = 0;
    switch (minicap->getCaptureMethod())
    {
    case Minicap::METHOD_FRAMEBUFFER:
        quirks |= QUIRK_DUMB | QUIRK_TEAR;
        break;
    case Minicap::METHOD_SCREENSHOT:
        quirks |= QUIRK_DUMB;
        break;
    case Minicap::METHOD_VIRTUAL_DISPLAY:
        quirks |= QUIRK_ALWAYS_UPRIGHT;
        break;
    }

    g_quirks = quirks;

    if (minicap->setRealInfo(realInfo) != 0)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Minicap did not accept real display info");
        goto disaster;
    }

    if (minicap->setDesiredInfo(desiredInfo) != 0)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Minicap did not accept desired display info");
        goto disaster;
    }

    minicap->setFrameAvailableListener(&gWaiter);

    if (minicap->applyConfigChanges() != 0)
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to start minicap with current config");
        goto disaster;
    }

    if (!encoder.reserveData(realInfo.width, realInfo.height))
    {
        print_debug_info(__FILE__, __LINE__, __FUNCTION__, "Unable to reserve data for JPG encoder");
        goto disaster;
    }

	print_debug_info(__FILE__, __LINE__, __FUNCTION__, "------------%dx%d------------\r\n", frame.width, frame.height);

    return EXIT_SUCCESS;

disaster:

    return EXIT_FAILURE;
}

int re_init_cap(int realInfo_width, int realInfo_height, int desiredInfo_width, int desiredInfo_height, int desiredInfo_orientation)
{
	print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%dx%d@%dx%d/%d\r\n", realInfo_width, realInfo_height, desiredInfo_width, desiredInfo_height, desiredInfo_orientation);
	if (minicap != NULL && !gWaiter.isStopped())
	{
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "reset minicap!!![%d]\r\n", pending);
		while (pending > 0) {
			minicap->releaseConsumedFrame(&frame);
			pending--;
		}
		// gWaiter.stop();
		minicap->release();
		minicap_free(minicap);
		minicap = NULL;
	}

	// encoder.release();

	return init_cap(realInfo_width, realInfo_height, desiredInfo_width, desiredInfo_height, desiredInfo_orientation);
}

jint JNICALL Java_jp_co_cyberagent_stf_App_set_1rotation(JNIEnv *, jobject, jint realInfo_width, jint realInfo_height, jint desiredInfo_width, jint desiredInfo_height, jint desiredInfo_orientation)
{
	print_debug_info(__FILE__, __LINE__, __FUNCTION__, "%dx%d@%dx%d/%d\r\n", realInfo_width, realInfo_height, desiredInfo_width, desiredInfo_height, desiredInfo_orientation);

	return re_init_cap(realInfo_width, realInfo_height, desiredInfo_width, desiredInfo_height, desiredInfo_orientation);
}

jint JNICALL Java_jp_co_cyberagent_stf_App_set_1quality(JNIEnv *, jobject, jint quality)
{
	print_debug_info(__FILE__, __LINE__, __FUNCTION__, "quality: %d\r\n", quality);

	return 0;
}

jint JNICALL Java_jp_co_cyberagent_stf_App_release_1cap(JNIEnv *, jobject)
{
    print_debug_info(__FILE__, __LINE__, __FUNCTION__, "release_cap called!!!");

    // if (haveFrame)
    // {
    //     minicap->releaseConsumedFrame(&frame);
    // }
    // print_debug_info(__FILE__, __LINE__, __FUNCTION__, "minicap_free called!!!");
    // minicap_free(minicap);

    // gWaiter.stop();

	if (minicap != NULL && !gWaiter.isStopped())
	{
		print_debug_info(__FILE__, __LINE__, __FUNCTION__, "release_cap!!![%d]\r\n", pending);
		while (pending > 0) {
			minicap->releaseConsumedFrame(&frame);
			pending--;
		}
		// gWaiter.stop();
		minicap->release();
		minicap_free(minicap);
		minicap = NULL;
	}

	encoder.release();

    return 0;
}
#endif

extern "C" jint JNICALL Java_jp_co_cyberagent_stf_App_init_1touch(JNIEnv *, jobject)
{
    return init_touch();
}

void JNICALL Java_jp_co_cyberagent_stf_App_set_1touch(JNIEnv *env, jobject, jbyteArray buffer)
{
    // jchar* _buffer = (char*)(env->GetByteArrayElements(buffer, NULL));
    char *_buffer = NULL;
    jbyte *bytes;
    bytes = env->GetByteArrayElements(buffer, 0);
    int chars_len = env->GetArrayLength(buffer);
    _buffer = new char[chars_len + 1];
    memset(_buffer,0,chars_len + 1);
    memcpy(_buffer, bytes, chars_len);
    _buffer[chars_len] = 0;

    env->ReleaseByteArrayElements(buffer, bytes, 0);

    // jchar* _buffer = env->GetCharArrayElements(buffer,0);
    if (_buffer != NULL)
    {
		print_touch_debug_info(__FILE__, __LINE__, __FUNCTION__,  "touch\t%s\n", _buffer);
        set_touch((char*)_buffer);
    }
}

jbyteArray JNICALL Java_jp_co_cyberagent_stf_App_get_1touch_1limits(JNIEnv *env, jobject)
{
	int length = 0;
	char* limits = get_limits();

	if (limits == NULL)
	{
		return NULL;
	}

	length = strlen(limits);
	if (length == 0)
	{
		return NULL;
	}

	jbyteArray c_result = env->NewByteArray(length);
	env->SetByteArrayRegion(c_result, 0, length, (jbyte*)limits);

	free(limits);
	limits = NULL;

	return c_result;
}

JNIEXPORT jint JNICALL Java_jp_co_cyberagent_stf_App_reset_1touch(JNIEnv *, jobject)
{
	return reset_touch();
}

JNIEXPORT jint JNICALL Java_jp_co_cyberagent_stf_App_free_1touch(JNIEnv *, jobject)
{
	free_touch();
}

void JNICALL Java_jp_co_cyberagent_stf_App_enable_1debug_1info(JNIEnv *, jobject, jboolean enable)
{
	enable_debug_info(enable);
}

void JNICALL Java_jp_co_cyberagent_stf_App_enable_1touch_1debug_1info(JNIEnv *, jobject, jboolean enable)
{
	enable_touch_debug_info(enable);
}

int argb2yuv420(unsigned char* argb_buff, int width, int height, unsigned char** yuv_buff, int* yuv_len) {
	unsigned char* yuv_y_buff = NULL;
	unsigned char* yuv_u_buff = NULL;
	unsigned char* yuv_v_buff = NULL;
	int len_src;
	int len_src_argb;
	int len_scale;

	print_debug_info(__FILE__, __LINE__, __FUNCTION__,"argb_buff: %08x %dx%d yuv_buff: %08x yuv_len: %08x", argb_buff, width, height, yuv_buff, yuv_len);

	if (argb_buff == NULL || yuv_buff == NULL || width <= 0 || height <= 0 || yuv_len == NULL) {
		return -1;
	}

	len_src = width * height * 3/2;
	len_scale = width * height * 3/2;
	*yuv_len = len_scale;

	yuv_y_buff = (unsigned char*)malloc(sizeof(char) * (len_src * 2 / 3));
	yuv_u_buff = (unsigned char*)malloc(sizeof(char) * (len_src / 6));
	yuv_v_buff = (unsigned char*)malloc(sizeof(char) * (len_src / 6));

	if (yuv_y_buff == NULL || yuv_u_buff == NULL || yuv_v_buff == NULL) {
		return -1;
	}

	if (*yuv_buff == NULL) {
		*yuv_buff = (unsigned char*)malloc(sizeof(char) * len_scale);
		memset(*yuv_buff, 0, len_scale);
		if (yuv_buff == NULL) {
			return -1;
		}
	}

	if (libyuv::ARGBToI420(argb_buff, width*4, yuv_y_buff, width, yuv_u_buff, width / 2, yuv_v_buff, width / 2, width, height) < 0) {
		return -1;
	}

	// merge y plane to output_data
	memcpy(*yuv_buff, yuv_y_buff, (len_src * 2 / 3));
	// merge v plane to output_data
	memcpy(*yuv_buff + (len_src * 2 / 3), yuv_u_buff, (len_src / 6));
	// merge u plane to output_data
	memcpy(*yuv_buff + (len_src * 5 / 6), yuv_v_buff, (len_src / 6));

	print_debug_info(__FILE__, __LINE__, __FUNCTION__,"%dx%d yuv_buff: %08x yuv_size: %d", width, height, *yuv_buff, *yuv_len);

	return 0;
}